package com.mychart.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mychart.constant.RedisKeyPrefixConstant;
import com.mychart.data.LoginUserSussionRepository;
import com.mychart.entity.domain.WsRes;
import com.mychart.entity.domain.WsRes.WsResType;
import com.mychart.entity.dto.UserDTO;
import com.mychart.entity.dto.UserFriendDTO;
import com.mychart.entity.mongo.ChartRecord;
import com.mychart.entity.po.ChartUserFriendPO;
import com.mychart.entity.po.ChartUserPO;
import com.mychart.mapper.ChartUserMapper;
import com.mychart.service.IChartUserFriendService;
import com.mychart.service.IChartUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.mychart.entity.domain.WsRes.WsResType.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author czh
 * @since 2023-10-16
 */
@Service
public class ChartUserServiceImpl extends ServiceImpl<ChartUserMapper, ChartUserPO> implements IChartUserService {

    @Autowired
    MongoTemplate mongoTemplate;
    @Autowired
    IChartUserFriendService userFriendService;
    @Autowired
    ChartUserMapper chartUserMapper;
    @Autowired
    LoginUserSussionRepository loginUserRepository;
    @Autowired
    RedisTemplate<String ,Object> redisTemplate;
    @Override
    public List<ChartUserPO> findUsersByName(String name) {
        if(Objects.equals(name, "")){
            return new ArrayList<>();
        }else {
            QueryWrapper<ChartUserPO> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("name",name);
            //TODO 过滤当前的用户。。 由于你没token 所以。额。暂时不考虑了吧。。
            return list(queryWrapper);
        }
    }

    @Override
    public void addFriend(UserFriendDTO userFriendDTO) {
        WebSocketSession friend = loginUserRepository.get(userFriendDTO.getFriendName());
        // if the friend is not online
        // add to the map
        boolean isAlreadyFriend = addNewFriendAdvice(userFriendDTO);
        if (friend != null && !isAlreadyFriend) {
            try {
                //TODO 格式化。 ws 的响应 和请求。。
                String res = JSON.toJSONString(new WsRes(true,"new friend！", NEW_FRIEND_ADVICE, userFriendDTO));
                friend.sendMessage(new TextMessage(res));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public List<UserDTO> getFriendApplyList(String userName) {
        String friendApplyRedisKey = getFriendApplyRedisKey(userName);
        Set<Object> members = redisTemplate.opsForSet().members(friendApplyRedisKey);
        List<UserDTO> list = new ArrayList<>();

        if (members != null){
            members.forEach(x -> {
                UserFriendDTO userFriendDTO = (UserFriendDTO) x;
                list.add(new UserDTO(userFriendDTO.getUserName()));
            });
        }
        return list;
    }

    @Override
    @Transactional
    public void confirmNewFriendApply(UserFriendDTO userFriendDTO) {
        //1 add to mysql
        userFriendService.save(new ChartUserFriendPO(userFriendDTO.getUserName(),userFriendDTO.getFriendName()));
        userFriendService.save(new ChartUserFriendPO(userFriendDTO.getFriendName(),userFriendDTO.getUserName()));
        //2 remove the redis cache
        String friendApplyRedisKey = getFriendApplyRedisKey(userFriendDTO.getUserName());
        redisTemplate.opsForSet().remove(friendApplyRedisKey,
                new UserFriendDTO(userFriendDTO.getFriendName(),userFriendDTO.getUserName()));
        //TODO in the future may advice the friend side.
    }

    @Override
    public List<UserDTO> getFriends(String userName) {
        QueryWrapper<ChartUserFriendPO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_name",userName);
        List<ChartUserFriendPO> list = userFriendService.list(queryWrapper);
        return list.stream().map(x -> new UserDTO(x.getFriendName())).collect(Collectors.toList());
    }

    @Override
    public List<ChartRecord> getChartRecord(UserFriendDTO userFriendDTO) {
        String userName = userFriendDTO.getUserName();
        String friendName = userFriendDTO.getFriendName();
        Query query = new Query();
        Criteria criteria = new Criteria();
        criteria.orOperator(
                Criteria.where("from").is(userName).and("to").is(friendName),
                Criteria.where("from").is(friendName).and("to").is(userName)
        );
        query.addCriteria(criteria);
        return mongoTemplate.find(query, ChartRecord.class);
    }

    /**
     *        QueryWrapper<ChartUserFriendPO> queryWrapper = new QueryWrapper<>();
     *         queryWrapper.eq("user_name",userName);
     *         List<ChartUserFriendPO> list = userFriendService.list(queryWrapper);
     *         return list.stream().map(x -> new UserDTO(x.getFriendName())).collect(Collectors.toList());
     */

    private boolean addNewFriendAdvice(UserFriendDTO userFriendDTO) {

        // 判断是否已经是好友。
        boolean exists = userFriendService.exists(new QueryWrapper<ChartUserFriendPO>().
                eq("user_name",userFriendDTO.getUserName()).
                eq("friend_name",userFriendDTO.getFriendName())
        );

        if(exists) {
            return true;
        }

        String redisKey = getFriendApplyRedisKey(userFriendDTO.getFriendName());
        Boolean isExist = redisTemplate.opsForSet().isMember(redisKey, userFriendDTO);
        if (Boolean.FALSE.equals(isExist)) {
            redisTemplate.opsForSet().add(redisKey,userFriendDTO);
        }
        return false;
    }

    /**
     * friendName's apply list
     * @param friendName
     * @return
     */
    private String getFriendApplyRedisKey(String friendName) {
        return RedisKeyPrefixConstant.NEW_FRIEND_ADVICE_REDIS_KEY_PREFIX + friendName;
    }
}
